import uuid
from rest_framework.decorators import api_view, permission_classes, action
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
from .models import System
from .serializers import SystemInfoSerializer, UserSerializer, PermSerializer, RoleSerializer
from services.system_collect import SystemService
from django.http import Http404
from rest_framework.views import Request, Response
from rest_framework.permissions import IsAuthenticated, BasePermission
from enum import Enum
from utils.exceptions import InvalidPassword
from django.contrib.auth import get_user_model
from rest_framework.filters import SearchFilter
from django.contrib.auth.models import Permission, ContentType, Group
from django.db.models import Q
from operator import or_
from functools import reduce


class SystemViewSet(ReadOnlyModelViewSet):
    queryset = System.objects.none()  # 只读，不实际查询数据库
    serializer_class = SystemInfoSerializer
    permission_classes = []  # 可按需添加权限

    @action(detail=False, methods=['get'])
    def info(self, request):
        """获取系统基本信息（带缓存）"""
        data = SystemService.get_current_system_info()
        serializer = self.get_serializer(data)
        return Response(serializer.data)

    @action(detail=False, methods=['get'])
    def metrics(self, request):
        """获取实时系统性能指标"""
        data = SystemService.get_system_metrics()
        serializer = self.get_serializer(data)
        return Response(serializer.data)

    @action(detail=False, methods=['post'])
    def refresh(self, request):
        """刷新系统信息（强制重新采集）"""
        data = SystemService.refresh_system_info()
        serializer = self.get_serializer(data)
        return Response(serializer.data)


class IsSuperUser(BasePermission):
    def has_permission(self, request, view):
        return bool(request.user and request.user.is_superuser)


class RoleViewSet(ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = RoleSerializer
    permission_classes = [IsSuperUser]
    filter_backends = [SearchFilter]
    search_fields = ['name']

    @action(detail=True)  # (?P<pk>[^/.]+)/perms/
    def perms(self, request, pk=None):
        # 用pk找到对应的数据，序列化返回
        instance = self.get_object()
        # data = RoleSerializer(instance).data  # 获得当前角色和其权限ids
        data = self.get_serializer(instance).data  # 两种写法都行
        data['allPerms'] = list(PermViewSet.queryset.values('id', 'name'))  # 返回所有权限，加list是为了json序列化
        return Response(data)


# 要排除的内建应用的所有id，只生成一次
_names = [
    ("admin", "logentry"),
    ("auth", "group"),
    ("auth", "permission"),
    ("contenttypes", "contenttype"),
    ("sessions", "session"),
    ('system', 'userprofile'),
    ('system', 'system'),
]

_buildins = reduce(or_, (Q(**{"app_label": k, "model": v}) for k, v in _names))  # 把Q对象用或连接
_exclusion = [c.id for c in ContentType.objects.filter(_buildins)]  # 排除的content_type ids


class PermViewSet(ReadOnlyModelViewSet):
    queryset = Permission.objects.exclude(content_type__in=_exclusion)
    serializer_class = PermSerializer
    permission_classes = []
    filter_backends = [SearchFilter]  # 支持模糊查询
    search_fields = ['name', 'codename']


class UserViewSet(ModelViewSet):
    queryset = get_user_model().objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsSuperUser]  # 必须是管理员才能管理用户
    filter_backends = [SearchFilter]
    search_fields = ['username']

    def partial_update(self, request, *args, **kwargs):
        black_list_fields = ['id', 'username', 'password']
        for fields in black_list_fields:
            request.data.pop(fields, None)  # 剔除不要更新的字段
        return super().partial_update(request, *args, **kwargs)

    def get_object(self):
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
        print(self.request.path)
        if self.request.method.lower() != 'get':
            pk = self.kwargs.get(lookup_url_kwarg)
            if pk == 1 or pk == '1':  # 超级管理员不能修改和删除，但可以修改自己的密码
                if 'chpwd' in self.request.path:
                    return super().get_object()
                raise Http404
            if self.request.method.lower() == 'delete':
                user = super().get_object()
                if user.id == self.request.user.id and user.is_superuser:  # id不为1的管理员不能删除自己
                    raise Http404
        return super().get_object()

    @action(detail=False, methods=['GET'], permission_classes=[IsAuthenticated])
    def whoami(self, request):
        # groups = request.user.groups.all() # 组
        perms = request.user.get_all_permissions()  # set
        userData = {'id': request.user.id, 'username': request.user.username, 'perms': perms}  # set被序列化为list
        return Response(userData)

    @action(['PATCH'], detail=True, permission_classes=[IsAuthenticated])
    def chpwd(self, request, pk=None):
        if int(pk) != request.user.id:
            raise Http404
        user = self.get_object()
        if user.check_password(request.data['oldpass']):
            user.set_password(request.data['newpass'])
            user.save()
            return Response()
        raise InvalidPassword

    @action(['PATCH'], detail=True)
    def setpwd(self, request, pk=None):
        user = self.get_object()
        if self.request.user.id == 1:
            user.set_password(request.data['password'])
            user.save()
            return Response()
        if self.request.user.is_superuser and self.request.user.id != 1:
            if int(pk) != self.request.user.id:
                user.set_password(request.data['password'])
                user.save()
                return Response()
            raise Http404
        raise Http404

    @action(detail=True)  # (?P<pk>[0-9]+)/roles/ GET
    def roles(self, request, pk=None):
        user = self.get_object()
        data = UserSerializer(user).data  # self.get_serializer(user).data
        data['roles'] = [r['id'] for r in user.groups.values('id')]  # pk关联用户的已有角色
        data['allRoles'] = [
            {
                "id": r.id,
                "name": r.name,
                "children": [{
                    # "id": -n['id'], # 子不能选，id也可以不给
                    "name": n['name'],
                    "disabled": True  # 子不能选，只是为了展示，前端中的属性
                } for n in r.permissions.values('id', 'name')],
            }
            for r in Group.objects.all()
        ]
        return Response(data)

    @roles.mapping.patch  # 路由同上 PATCH
    def set_roles(self, request, pk=None):
        user = self.get_object()
        roles = request.data.get('roles', [])
        user.groups.set(roles)  # sql语句执行
        return Response(status=201)


# 使用枚举类，构建前端图标
class MenuIcon(Enum):
    HOMEFILLED ='HomeFilled'
    SHARE = 'Share'
    TICKET = 'Ticket'


class MenuItem(dict):
    def __init__(self, id, name, path: str = None, icon: str = None):
        super().__init__()
        self['id'] = id
        self['name'] = name
        self['path'] = path if path else f"/{uuid.uuid4().hex}"
        self['icon'] = icon
        self['children'] = []

    def append(self, item):
        if isinstance(item, MenuItem):
            self['children'].append(item)
        else:
            raise TypeError(f"{item}必须是{MenuItem.__class__.__name__}")


@api_view()  # 默认GET
@permission_classes([IsAuthenticated])  # 当前视图自己的权限设置，覆盖全局配置
def menulist_view(request: Request):
    menulist = []
    if request.user.is_superuser:  # 管理员才能获取到的菜单
        item1 = MenuItem(1, '系统管理', icon=MenuIcon.HOMEFILLED.value)
        item1.append(MenuItem(101, 'dashboard', '/system/dashboard'))
        item1.append(MenuItem(101, '用户列表', '/system/user'))
        item1.append(MenuItem(102, '权限列表', '/system/perms'))
        item1.append(MenuItem(103, '角色列表', '/system/roles'))
        menulist.append(item1)

    if request.user.has_perm('tree.view_organization'):
        item2 = MenuItem(2, '组织树', icon=MenuIcon.SHARE.value)
        item2.append(MenuItem(201, '资产管理', '/tree/orgs'))
        item2.append(MenuItem(202, '审计管理', '/tree/tracks'))
        menulist.append(item2)

    if request.user.has_perm('iac.view_repository'):
        item3 = MenuItem(3, 'IAC基础设施', icon=MenuIcon.TICKET.value)
        item3.append(MenuItem(301, '项目管理', '/iac/repos'))
        item3.append(MenuItem(302, '项目历史', '/iac/his'))
        menulist.append(item3)

    return Response(menulist)
